package com.gitee.feizns.quickstart.code.generator;

import com.gitee.feizns.dynamic.Sets;
import com.gitee.feizns.dynamic.io.FileUtils;
import com.gitee.feizns.dynamic.io.Ios;
import com.gitee.feizns.quickstart.mustache.Mustaches;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 代码生成器
 * <pre>{@code
 *     public static void main(String[] args) {
 *         CodeGenerator.of("/api/v1", Hello.class)
 *                 .register(Methods.crud()).register(Methods.SimpleList)
 *                 .generate().all();
 *     }
 * }</pre>
 * @author feizns
 * @since 2024/12/5
 */
@Accessors(chain = true)
public class CodeGenerator {

    /**
     * 代码生成器
     * @param pojo POJO
     * @return {@link CodeGenerator}
     */
    public static CodeGenerator of(Class<?> pojo) {
        return new CodeGenerator("/api", pojo);
    }

    /**
     * 代码生成器
     * @param pojo POJO
     * @return {@link CodeGenerator}
     */
    public static CodeGenerator of(String api, Class<?> pojo) {
        return new CodeGenerator(api, pojo);
    }

    /**
     * mybatis plus templates 目录
     */
    public static final String MybatisPlusTemplatesDir = "/com/gitee/feizns/quickstart/code/generator/templates/mybatis/plus";

    /**
     * mybatis flex templates 目录
     */
    public static final String MybatisFlexTemplateDir = "/com/gitee/feizns/quickstart/code/generator/templates/mybatis/flex";

    /**
     * 工作目录
     */
    public final File workspace;

    /**
     * 源码目录
     */
    public final File sourceCodeDirectory;

    /**
     * module 包路径
     */
    @Getter
    private final File modulePackagePath;

    /**
     * module 包名
     */
    @Getter
    private final String modulePackageName;

    /**
     * 实体类元数据
     */
    @Getter
    private final PojoMetadata pojoMetaData;

    /**
     * POJO 简单名称
     */
    private final String pojoSimpleName;

    /**
     * 应用程序接口
     */
    @Setter
    @Getter
    private String api;

    /**
     * 验证
     */
    private boolean validate = true;

    /**
     * 禁用验证
     * @return {@link CodeGenerator}
     */
    public CodeGenerator disableValidate() {
        this.validate = false;
        return this;
    }

    /**
     * 代码生成器
     * @param pojo POJO
     */
    public CodeGenerator(Class<?> pojo) {
        this(null, pojo);
    }

    /**
     * 代码生成器
     * @param pojo POJO
     * @param api 应用程序接口
     */
    public CodeGenerator(String api, Class<?> pojo) {
        this.api = api;
        //获取执行代码所在的工程目录
        this.workspace = new File(Objects.requireNonNull(pojo.getResource("/")).getFile()).getParentFile().getParentFile();
        //获取工程源码目录
        this.sourceCodeDirectory = new File(workspace, "/src/main/java");
        String pojoPackageName = pojo.getPackage().getName();
        this.modulePackageName = pojoPackageName.substring(0, pojoPackageName.lastIndexOf(".domain.pojo"));
        this.modulePackagePath = new File(sourceCodeDirectory, modulePackageName.replace(".", File.separator));
        this.pojoMetaData = new PojoMetadata(pojo);
        this.pojoSimpleName = pojoMetaData.getPojo().getSimpleName();
    }

    /**
     * 数据
     * @return {@link Map}<{@link String}, {@link Object}>
     */
    private Map<String, Object> data() {
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("api", api);
        data.put("date", LocalDate.now().toString());
        data.put("pojo", pojoMetaData);
        data.put("module.name", modulePackageName);
        for (Methods value : Methods.values())
            data.put("methods.has." + value.name(), methods.contains(value));
        data.put("methods.has.QueryAo", contains(Methods.Page, Methods.Patch, Methods.Delete));
        data.put("methods.has.GetMapping", contains(Methods.Page, Methods.GetById));
        data.put("methods.has.PostMapping", contains(Methods.Create));
        data.put("methods.has.PatchMapping", contains(Methods.PatchById, Methods.Patch));
        data.put("methods.has.PutMapping", contains(Methods.PutById, Methods.PutById));
        data.put("methods.has.DeleteMapping", contains(Methods.DeleteById, Methods.Delete));
        data.put("methods.has.PathVariable", contains(Methods.GetById, Methods.PatchById, Methods.PutById, Methods.DeleteById));
        data.put("methods.has.RequestBody", contains(Methods.Create, Methods.Patch, Methods.Put));
        data.put("methods.has.Valid", contains(Methods.Creates, Methods.Patch, Methods.Put));
        data.put("validate", validate);
        return data;
    }

    /**
     * 方法
     */
    private final Set<Methods> methods = Sets.of(Methods.crud());

    /**
     * 注册
     * @param methods 方法
     * @return {@link CodeGenerator }
     */
    public CodeGenerator register(Methods... methods) {
        this.methods.addAll(Sets.of(methods));
        return this;
    }

    /**
     * 包含
     * @return boolean
     */
    public boolean contains(Methods... methods) {
        for (Methods method : methods) {
            if ( this.methods.contains(method) )
                return true;
        }
        return false;
    }

    /**
     * 生成mybatis flex
     * @return {@link Generator}
     */
    @SneakyThrows
    public Generator generateWithMybatisFlexTemplate() {
        return generate(CodeGenerator.class, MybatisFlexTemplateDir);
    }

    /**
     * 生成mybatis plus
     * @return {@link Generator}
     */
    @SneakyThrows
    public Generator generateWithMybatisPlusTemplate() {
        return generate(CodeGenerator.class, MybatisPlusTemplatesDir);
    }

    /**
     * 生成
     * @param classpath 类路径
     * @return {@link Generator}
     */
    @SneakyThrows
    public Generator generate(Class<?> classpath) {
        return new Generator(classpath, "/");
    }

    /**
     * 生成
     * @param templatesDir templates 目录
     * @return {@link Generator}
     */
    @SneakyThrows
    public Generator generate(Class<?> classpath, String templatesDir) {
        return new Generator(classpath, templatesDir);
    }

    /**
     * 使用字符串模板写入
     * @param template 模板（字符串模板）
     * @param filename 目标文件路径（在模块路径后的文件路径）（生成的目标文件路径）
     */
    @SneakyThrows
    public CodeGenerator generate(String template, String filename) {
        Map<String, Object> data = data();
        String text = Mustaches.execute(template, data);
        FileUtils.write(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)), new File(modulePackagePath, filename));
        System.out.println("[" + modulePackageName + "." + filename.replace("/", ".").replace(".java", "") + "]" + "].写入成功");
        return this;
    }

    /**
     * 生成器
     * @author feizns
     * @since 2024/12/06
     */
    public class Generator {

        /**
         * 以改类作为基准读取模板
         */
        private final Class<?> classpath;

        /**
         * 目录
         */
        private final String dir;

        /**
         * 生成器
         * @param dir 目录
         * @param classpath 类路径
         */
        public Generator(Class<?> classpath, String dir) {
            this.classpath = classpath;
            this.dir = dir.charAt(dir.length() - 1) == File.separatorChar ? dir.substring(0, dir.length() - 1) : dir;
        }

        /**
         * 生成所有
         * @return {@link Generator}
         */
        public Generator all() {
            return controller().service().mapper().ao();
        }

        /**
         * 写入Controller
         * @return {@link Generator}
         */
        public Generator controller() {
            withTemplateWriteTo("/Controller", "controller/" + pojoSimpleName + "Controller.java");
            return this;
        }

        /**
         * 写入Service
         * @return {@link Generator}
         */
        public Generator service() {
            withTemplateWriteTo("/Service", "service/" + pojoMetaData.getPojo().getSimpleName() + "Service.java");
            withTemplateWriteTo("/ServiceImpl", "service/impl/" + pojoMetaData.getPojo().getSimpleName() + "ServiceImpl.java");
            return this;
        }

        /**
         * 生成Mapper
         * @return {@link Generator}
         */
        public Generator mapper() {
            withTemplateWriteTo("/Mapper", "mapper/" + pojoSimpleName + "Mapper.java");
            return this;
        }

        /**
         * 控制器
         * @return {@link Generator}
         */
        public Generator ao() {
            //Creates
            if ( methods.contains(Methods.Create) )
                withTemplateWriteTo("/ao/CreateAo", "domain/ao/" + pojoSimpleName + "CreateAo.java");
            //PatchById
            if ( methods.contains(Methods.PatchById) )
                withTemplateWriteTo("/ao/PatchByIdAo", "domain/ao/" + pojoSimpleName + "PatchByIdAo.java");
            //Patches
            if ( methods.contains(Methods.Patch) )
                withTemplateWriteTo("/ao/PatchAo", "domain/ao/" + pojoSimpleName + "PatchAo.java");
            return this;
        }

        /**
         * 使用模板写入到
         * @return {@link Generator}
         */
        public Generator withTemplateWriteTo(String templatePath, String filename) {
            generate(Ios.asString(classpath.getResourceAsStream(dir + templatePath)), filename);
            return this;
        }

    }

}
